//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "cgmodule.h"
#include "pub.h"

#define DLL_PROCESS_DETACH    0
#define DLL_PROCESS_ATTACH    1

void GUnloadModule(GModule * pModule);

extern Aura_Handle  g_hAttachLock;

Aura_Bool GModule::AllocMemery(Aura_UInt32 uImageBase, Aura_UInt32 uTotalSize)
{
    Aura_ECode ec = AuraVirtualMemory_Map(0, uTotalSize, \
            Aura_MemoryMap_Upgrow, Aura_MemoryProtection_RW, &m_uImageBase);
            
    if (AURA_FAILED(ec)) {
        Aura_printf("Error: Map memery to moudle fail! file:%s. line:%d.\n", __FILE__, __LINE__);
        return AURA_FALSE;
    }
            
//    Aura_printf("Alloc module image base:0x%x.\n", m_uImageBase);
    return AURA_TRUE;
}

typedef Aura_Bool  (*DLLMAINCRTSTARTUP)(Aura_PVoid hDllHandle, int dwReason, Aura_PVoid preserved);

Aura_Bool GModule::AttachProcess(Aura_PVoid preserved)
{
    DLLMAINCRTSTARTUP entry;
    Aura_Bool ret = AURA_TRUE;
    if(ModuleTachStatus_INIT != m_attached){
        return AURA_TRUE;
    }

    if(m_uEntryPoint && (ModuleFileType_DLL == m_fileType)){
        entry = (DLLMAINCRTSTARTUP)m_uEntryPoint;
        ret = entry((Aura_PVoid)m_uImageBase, DLL_PROCESS_ATTACH, preserved);
        if(!ret){
            ret =  AURA_FALSE;
        }
    }
    m_attached = ModuleTachStatus_ATTACHED;
    return ret;
}

Aura_Bool GModule::DetachProcess(Aura_PVoid preserved)
{
    DLLMAINCRTSTARTUP entry;
    Aura_Bool ret = AURA_TRUE;

    if(ModuleTachStatus_ATTACHED != m_attached){
        return AURA_TRUE;
    }

    if(m_uEntryPoint && (ModuleFileType_DLL == m_fileType)){
        entry = (DLLMAINCRTSTARTUP)m_uEntryPoint;
        if(!entry((Aura_PVoid)m_uImageBase, DLL_PROCESS_DETACH, preserved)){
            ret =  AURA_FALSE;
        }
    }
    m_attached = ModuleTachStatus_DETACHED;
    return ret;
}

Aura_UInt32 GModule::AddRef(void)
{
    Aura_Int32 lRef = m_cRef.Increment();
    return lRef;
}

Aura_UInt32 GModule::Release(void)
{
    Aura_Int32 lRef = m_cRef.Decrement();
    
    assert(lRef >= 0);
    if (0 == lRef) {
        Lock(g_hAttachLock);
        DetachProcess(AURA_NULL);
        Unlock(g_hAttachLock);
        GUnloadModule(this);
        delete this;
    }
    return (Aura_UInt32)lRef;
}

GModule::~GModule()
{  
    ModuleRef * pModRef;

    pModRef = (ModuleRef *)m_rModuleList.m_pNext;
    while ((DLinkNode *)pModRef != &m_rModuleList) {
        pModRef->Detach();
        pModRef->m_pModule->Release();
        delete pModRef;
        pModRef = (ModuleRef *)m_rModuleList.m_pNext;
    }
}

EXTERN_C Aura_Bool ProcessAttach(GModule *pModule, Aura_PVoid preserved)
{
    ModuleRef *pModRef;
    Aura_Bool ret = AURA_TRUE;

    ForEachDLinkNode(ModuleRef *, pModRef, &(pModule->m_rModuleList)) {
        ret = ProcessAttach(pModRef->m_pModule, preserved);
        if(!ret) return ret;
    }
    ret = pModule->AttachProcess(preserved);
    return ret;
}

EXTERN_C void ProcessDetach(DLinkNode *pModuleList, Aura_PVoid preserved)
{
    GModule *pModule;

    ForEachDLinkNode(GModule *, pModule, pModuleList) {
        pModule->DetachProcess(preserved);
    }
}
